import 'package:dio/dio.dart';
import 'package:flutter_enjoy_plus/constants/index.dart';
import 'package:flutter_enjoy_plus/utils/Emitter.dart';
import 'package:flutter_enjoy_plus/utils/TokenManageer.dart';
import 'package:flutter_enjoy_plus/utils/toast.dart';

class RequestDio {
  // dio 实例
  final Dio _dio = Dio();

  RequestDio() {
    // 设置请求的根路径
    // _dio.options.baseUrl = GlobalVariable.BASE_URL;
    // // 设置请求的连接超时时间
    // _dio.options.connectTimeout =
    //     const Duration(seconds: GlobalVariable.NET_WORK_TIME);
    // // 设置请求的接收超时时间
    // _dio.options.receiveTimeout =
    //     const Duration(seconds: GlobalVariable.NET_WORK_TIME);

    // flutter 语法 .. 链式调用
    _dio.options
      ..baseUrl = GlobalVariable.BASE_URL // 设置请求的根路径
      ..connectTimeout =
          const Duration(seconds: GlobalVariable.NET_WORK_TIME) // 设置请求的连接超时时间
      ..receiveTimeout =
          const Duration(seconds: GlobalVariable.NET_WORK_TIME); // 设置请求的接收超时时间

    // 拦截器
    _interceptors();
  }

  // 拦截器
  void _interceptors() {
    // 请求拦截器
    _dio.interceptors.add(
      InterceptorsWrapper(
        // 请求拦截器
        onRequest: (options, handler) {
          // 自动添加 token
          final token = tokenManager.getToken();
          // 如果 token 存在，则添加到请求头
          if (token.isNotEmpty) {
            options.headers['Authorization'] = 'Bearer $token';
          }
          handler.next(options);
        },
        // 响应拦截器 200 >= HTTP状态码 < 300
        onResponse: (response, handler) {
          handler.next(response);
        },
        // 错误拦截器 400 <= HTTP状态码 < 500
        onError: (error, handler) async {
          // 如果 token 过期，则触发退出登录事件
          if (error.response?.statusCode == 401) {
            // 1. 获取刷新 token
            String refreshToken = tokenManager.getRefreshToken();
            // 如果刷新 token 不存在，则退出登录
            if (refreshToken.isEmpty) {
              // 删除 token
              tokenManager.deleteToken();
              // 非页面，无法获取到 context，所以无法跳转
              // Navigator.pushNamed(context, '/login');
              // 触发退出登录事件，目的是调用页面跳转
              eventBus.fire(LogoutEvent());
            } else {
              // 如果有刷新 token，重新换取新的访问 token
              final isOk = await _refreshToken();
              // 无感重新发送请求
              if (isOk) {
                // 重新发送请求
                final res = await _dio.fetch(error.requestOptions);
                handler.resolve(res); // 标记为成功
                return;
              } else {
                tokenManager.deleteToken();
                eventBus.fire(LogoutEvent());
              }
            }
          }
          // 错误提示
          PromptAction.showError(error.response?.data['message'] ?? '请求失败');
          handler.next(error);
        },
      ),
    );
  }

  // 刷新 token 核心业务
  Future<bool> _refreshToken() async {
    try {
      // 1. 获取刷新 token
      String refreshToken = tokenManager.getRefreshToken();
      // 2. 创建新的请求对象
      Dio dio = Dio();
      dio.options
        ..baseUrl = GlobalVariable.BASE_URL
        ..connectTimeout = const Duration(seconds: GlobalVariable.NET_WORK_TIME)
        ..receiveTimeout = const Duration(seconds: GlobalVariable.NET_WORK_TIME)
        ..headers['Authorization'] = 'Bearer $refreshToken';
      // 3. 请求刷新 token 接口
      final res = await dio.post('/refreshToken');
      // 4. 判断是否刷新成功
      if (res.data['code'] == GlobalVariable.SUCCESS_CODE) {
        // 5. 更新 token
        tokenManager.setToken(
          res.data['data']['token'],
          res.data['data']['refreshToken'],
        );
        // 返回 true，表示 token 续期成功
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  }

  // 请求方法二次封装
  // 请求三要素
  //   1. 请求地址(服务器地址 + 接口路径)
  //   2. 请求方法(restful API 风格: get(查询)、post(创建)、delete(删除)、put(批量更新)、patch(局部更新))
  //   3. 请求参数(路径参数、查询参数、请求体参数、请求头参数)
  Future get(String url, {Map<String, dynamic> params = const {}}) async {
    final response = await _dio.get(url, queryParameters: params);
    return _handleResponse(response);
  }

  Future post(String url, {Map<String, dynamic> data = const {}}) async {
    final response = await _dio.post(url, data: data);
    return _handleResponse(response);
  }

  Future put(String url, {Map<String, dynamic> data = const {}}) async {
    final response = await _dio.put(url, data: data);
    return _handleResponse(response);
  }

  Future delete(String url, {Map<String, dynamic> data = const {}}) async {
    final response = await _dio.delete(url, data: data);
    return _handleResponse(response);
  }

  Future patch(String url, {Map<String, dynamic> data = const {}}) async {
    final response = await _dio.patch(url, data: data);
    return _handleResponse(response);
  }

  // 上传文件，参数格式为 FormData
  Future upload(String url, {FormData? data}) async {
    final response = await _dio.post(url, data: data);
    return _handleResponse(response);
  }

  // 响应数据处理
  _handleResponse(Response response) {
    // 检查业务状态码是否 10000 成功
    if (response.data['code'] == GlobalVariable.SUCCESS_CODE) {
      return response.data['data'];
    } else {
      // 业务状态码错误提示
      PromptAction.showError(response.data['message'] ?? '业务异常');
    }
    throw Exception(response.data['message']);
  }
}

// 单例(单个实例)模式
final requestDio = RequestDio();
